package com.px.project.module.ht.service.impl.websocket;

import cn.hutool.core.util.ObjectUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import com.px.common.config.cache.UserCache;
import com.px.common.config.security.SysLoginUser;
import com.px.common.enums.WebSocketEnum;
import org.springframework.stereotype.Component;

import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * 系统管理》生产调度》大屏业务逻辑实现
 *
 * @author 品讯科技
 * @date 2024-08
 */
@Slf4j
@Component
@ServerEndpoint("/websocket/{token}")
public class WebSocketService {
    private static final Long SESSION_TIME_OUT = 600000L;
    /**
     * 记录当前在线连接数
     */
    private static AtomicInteger ON_LINE_SESSION_CLIENT_COUNT = new AtomicInteger(0);
    /**
     * 存放在线用户客户端
     */
    private static Map<String, Session> ON_LINE_SESSION_CLIENT_MAP = new ConcurrentHashMap<>();
    /**
     * 用户token
     */
    private String token;
    /**
     * 用户会话
     */
    private Session session;

    /**
     * 建立连接
     *
     * @param token 每次页面建立连接时传入到服务端的id，比如：用户id
     * @param session 与某个客户端的连接会话，需要通过它来给客户端发送消息
     */
    @OnOpen
    public void onOpen(@PathParam("token") String token, Session session) {
        SysLoginUser sysLoginUser = (SysLoginUser)UserCache.getLoginUser(token);
        if (ObjectUtil.isEmpty(sysLoginUser)) {
            // TODO 用户过期后续操作
        }
        log.info("连接建立中，sessionId = {}， userId = {}", session.getId(), token);
        Session historySession = ON_LINE_SESSION_CLIENT_MAP.get(token);
        if (ObjectUtil.isNotEmpty(historySession)) {
            ON_LINE_SESSION_CLIENT_MAP.remove(token);
            ON_LINE_SESSION_CLIENT_COUNT.decrementAndGet();
        }
        session.setMaxIdleTimeout(SESSION_TIME_OUT);
        this.token = token;
        this.session = session;
        ON_LINE_SESSION_CLIENT_MAP.put(token, session);
        ON_LINE_SESSION_CLIENT_COUNT.incrementAndGet();
        Map<String, String> map = new HashMap<>(1);
        map.put("message", WebSocketEnum.SUCCESSFU_LCONNECTION.getMessage());
        sendToOne(token, JSON.toJSONString(map));
        log.info("连接建立成功，当前在线数为：{}", ON_LINE_SESSION_CLIENT_COUNT);
    }

    /**
     * 关闭连接
     *
     * @param token 每次页面建立连接时传入到服务端的id，比如：用户id
     * @param session 与某个客户端的连接会话，需要通过它来给客户端发送消息
     */
    @OnClose
    public void onClose(@PathParam("token") String token, Session session) {
        log.info("连接关闭中，sessionId = {}， token = {}", session.getId(), token);
        ON_LINE_SESSION_CLIENT_MAP.remove(token);
        ON_LINE_SESSION_CLIENT_COUNT.decrementAndGet();
        log.info("连接关闭成功，当前在线数为：{}", ON_LINE_SESSION_CLIENT_COUNT);
    }

    /**
     * 收到客户端消息
     *
     * @param message 消息内容
     * @param session 与某个客户端的连接会话，需要通过它来给客户端发送消息
     */
    @OnMessage
    public void onMessage(String message, Session session) {
        log.info("服务端收到客户端消息：{}", message);
        JSONObject jsonObject = JSON.parseObject(message);
        String sendToken = jsonObject.getString("token");
        String messageData = jsonObject.getString("message");
        if (ObjectUtil.isEmpty(sendToken)) {
            sendToAll(messageData);
        } else {
            sendToOne(sendToken, messageData);
        }
    }

    /**
     * 发生异常
     *
     * @param session 与某个客户端的连接会话，需要通过它来给客户端发送消息
     * @param error 异常
     */
    @OnError
    public void onError(Session session, Throwable error) {
        log.error("WebSocket发生异常：{}", error.getMessage());
        error.printStackTrace();
    }

    /**
     * 群发消息
     *
     * @param message 消息内容
     */
    public void sendToAll(String message) {
        ON_LINE_SESSION_CLIENT_MAP.forEach((onlineToken, sendSession) -> {
            if (ObjectUtil.notEqual(token, onlineToken)) {
                log.info("服务端给客户-{}-发群消息：{}", onlineToken, message);
                sendSession.getAsyncRemote().sendText(message);
            }
        });
    }

    /**
     * 单个发送消息
     *
     * @param token 每次页面建立连接时传入到服务端的id，比如：用户id
     * @param message 消息内容
     */
    public void sendToOne(String token, String message) {
        Session sendSession = ON_LINE_SESSION_CLIENT_MAP.get(token);
        if (ObjectUtil.isEmpty(sendSession)) {
            log.error("用户-{}-已移除列表", token);
            return;
        }
        log.info("服务端给客户-{}-发消息：{}", token, message);
        sendSession.getAsyncRemote().sendText(message);
    }
}